সি প্রোগ্রামিং ভাষার মধ্যে কিছু অতিরিক্ত গুরুত্বপূর্ণ ধারণা এবং ফিচার রয়েছে যা সাধারন প্রোগ্রামিং কৌশল ও কার্যকারিতা ছাড়াও বিশেষ কিছু ফিচার প্রদান করে। এই ধারণাগুলি অন্যান্য প্রোগ্রামিং ভাষায় তুলনামূলকভাবে আলাদা এবং সি ভাষায় তাদের ব্যবহার গুরুত্বপূর্ণ। এখানে কিছু অতিরিক্ত সি অন্যান্য বৈশিষ্ট্য নিয়ে আলোচনা করা হবে।
স্ট্রাকচার সি প্রোগ্রামিং ভাষায় একটি কম্পোজিট ডেটা টাইপ (composite data type) যা একাধিক ভিন্ন ধরনের ডেটা সদস্যকে একত্রে রাখে। স্ট্রাকচারের সাহায্যে বিভিন্ন ধরনের ডেটা একত্রে একটি ইউনিটের মধ্যে রাখা যায়।
#include <stdio.h>
// Define a structure for a point in 2D
struct Point {
int x;
int y;
};
int main() {
struct Point p1; // Declare a variable of type struct Point
// Assign values to the structure members
p1.x = 10;
p1.y = 20;
printf("Point Coordinates: (%d, %d)\n", p1.x, p1.y);
return 0;
}
ব্যাখ্যা:
struct Point
একটি স্ট্রাকচার ঘোষণা করেছে যা দুটি সদস্য (x
এবং y
) ধারণ করে, যেগুলো int টাইপের।p1
হল স্ট্রাকচার টাইপের একটি ভেরিয়েবল, এবং তার x
এবং y
সদস্যকে মান দেওয়া হয়েছে।ইউনিয়ন সি প্রোগ্রামের আরেকটি ডেটা টাইপ যা স্ট্রাকচারের মতো হলেও এতে সব সদস্য একই মেমরি লোকেশনে সংরক্ষিত থাকে, যার ফলে কম মেমরি ব্যবহার করা হয়। ইউনিয়নটি একসাথে একটাই সদস্য ধরে রাখতে সক্ষম।
#include <stdio.h>
// Define a union
union Data {
int i;
float f;
char c;
};
int main() {
union Data data; // Declare a variable of type union Data
// Assign values to union members
data.i = 10;
printf("data.i: %d\n", data.i);
data.f = 220.5;
printf("data.f: %.2f\n", data.f);
data.c = 'A';
printf("data.c: %c\n", data.c);
// Notice that only the last assigned value is valid
return 0;
}
ব্যাখ্যা:
union Data
একটি ইউনিয়ন ঘোষণা করেছে যা তিনটি ভিন্ন ধরনের ডেটা সদস্য ধারণ করে।data
নামক ভেরিয়েবলের মধ্যে শুধুমাত্র একটি সদস্য থাকবে যেটি শেষবারে পরিবর্তন করা হয়েছে, কারণ ইউনিয়নে সব সদস্য একই মেমরি লোকেশনে সংরক্ষিত থাকে।সি প্রোগ্রামে পয়েন্টার ব্যবহার করে মেমরি অ্যাক্সেস এবং ডেটা ম্যানিপুলেশন সহজে করা যায়। পয়েন্টার আরিথমেটিকের মাধ্যমে পয়েন্টারের মান পরিবর্তন করা যায় এবং মেমরি ঠিকানা হিসেবে গণনা করা হয়।
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // Pointer to the first element of the array
printf("First element: %d\n", *ptr);
ptr++; // Move the pointer to the next element
printf("Second element: %d\n", *ptr);
ptr += 2; // Move the pointer two elements ahead
printf("Fourth element: %d\n", *ptr);
return 0;
}
ব্যাখ্যা:
ptr++
: এটি পয়েন্টার ptr
কে পরবর্তী মেমরি লোকেশনে সরিয়ে নিয়ে যায়।ptr += 2
: এটি পয়েন্টারকে দুইটি মেমরি অবস্থান এগিয়ে নিয়ে যায়, অর্থাৎ অ্যারে arr[]
এর পরবর্তী দুটি উপাদান একযোগে অ্যাক্সেস করতে সাহায্য করে।সি প্রোগ্রামে ফাইল হ্যান্ডলিং অত্যন্ত গুরুত্বপূর্ণ। এর মাধ্যমে ফাইল খুলতে, পড়তে, লিখতে এবং বন্ধ করতে বিভিন্ন ফাংশন ব্যবহার করা হয়। কিছু গুরুত্বপূর্ণ ফাংশন হলো fopen()
, fclose()
, fread()
, fwrite()
, fprintf()
, এবং fscanf()
।
#include <stdio.h>
int main() {
FILE *fptr;
fptr = fopen("test.txt", "w"); // Open file in write mode
if (fptr == NULL) {
printf("Error opening file!\n");
return 1;
}
fprintf(fptr, "Hello, file handling in C!\n"); // Writing to file
fclose(fptr); // Close the file
return 0;
}
ব্যাখ্যা:
fopen("test.txt", "w")
: এটি একটি ফাইল খুলবে লেখার জন্য। যদি ফাইলটি আগে থেকেই না থাকে তবে এটি নতুন ফাইল তৈরি করবে।fprintf(fptr, ...)
: এটি ফাইলের মধ্যে কিছু লেখা হবে।fclose(fptr)
: ফাইলটি বন্ধ করা হবে।সি ভাষায় ডাইনামিক মেমরি অ্যালোকেশন ব্যবহার করে প্রোগ্রাম চলাকালীন সময়ে মেমরি বরাদ্দ করা যায়। সি প্রোগ্রামে malloc(), calloc(), realloc(), এবং free() ফাংশন ব্যবহার করা হয়।
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
ptr = (int*)malloc(5 * sizeof(int)); // Allocate memory for 5 integers
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
for (int i = 0; i < 5; i++) {
ptr[i] = i + 1; // Initialize the allocated memory
}
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]); // Print the values
}
free(ptr); // Free the allocated memory
return 0;
}
ব্যাখ্যা:
malloc(5 * sizeof(int))
: এটি ৫টি পূর্ণসংখ্যার জন্য মেমরি বরাদ্দ করে।free(ptr)
: এটি ডাইনামিকভাবে বরাদ্দ করা মেমরি মুক্ত করে।সি প্রোগ্রামে কোড অপটিমাইজেশন হল প্রোগ্রামের গতি এবং মেমরি ব্যবহারের সর্বোত্তম ব্যবহার নিশ্চিত করার প্রক্রিয়া। এটি লুপ অপটিমাইজেশন, পয়েন্টার ব্যবহার, এবং অপ্রয়োজনীয় মেমরি বরাদ্দ এড়ানোর মাধ্যমে করা যেতে পারে।
সি প্রোগ্রামিং ভাষা স্ট্রাকচার, ইউনিয়ন, পয়েন্টার আরিথমেটিক, ফাইল হ্যান্ডলিং, ডাইনামিক মেমরি অ্যালোকেশন, এবং কোড অপটিমাইজেশন এর মতো শক্তিশালী বৈশিষ্ট্য প্রদান করে, যা প্রোগ্রাম লেখার সময় কাস্টমাইজড সমাধান এবং উচ্চ কার্যকারিতা অর্জন করতে সহায়তা করে।
এই অধ্যায়ে আপনি সি এর স্টান্ডার্ড লাইব্রেরী ফাংশন সম্মন্ধে জানবেন। আরোও স্পষ্টভাবে বললেঃ লাইব্রেরী ফাংশন কি, বিভিন্ন প্রকার লাইব্রেরী ফাংশন এবং আপনার প্রোগ্রামে এগুলো কিভাবে ব্যবহার করবেন।
সি লাইব্রেরী ফাংশনের প্রোটোটাইপ(prototype) এবং সংজ্ঞা(definition)-সমূহ রয়েছে স্ব-স্ব হেডার ফাইলে। আপনার প্রোগ্রাম থেকে এগুলোকে এক্সেস করতে হলে অবশ্যই এই হেডার ফাইল-সমূহকে আপনার প্রোগ্রামে ডিপেন্ডেন্সি(dependency) হিসাবে সংযুক্ত করতে হবে।
উদাহরণস্বরূপঃ আপনি যদি printf()
ফাংশনকে আপনার প্রোগ্রামে ব্যবহার করতে চান তাহলে <stdio.h>
হেডার ফাইলকে আপনার প্রোগ্রামে অবশ্যই সংযুক্ত করতে হবে।
#include <stdio.h>
int main()
{
/* আপনি যদি <stdio.h> হেডার ফাইল সংযুক্ত না করে printf() ফাংশন
ব্যবহার করেন তাহলে আপনার প্রোগ্রাম error দেখাবে। */
printf(" stdio.h is a required header file for print funtion.");
}
সি প্রোগ্রাম main()
ফাংশন দিয়ে শুরু হয় এবং এই ফাংশনটিও লাইব্রেরী ফাংশন এর অন্তর্ভুক্ত। আপনার প্রোগ্রাম শুরু হলে এই ফাংশনটি স্বয়ংক্রিয়ভাবেই কল(call) হয়।
সি প্রোগ্রামে রয়েছে অসংখ্য লাইব্রেরী ফাংশন যা আপনার প্রোগ্রামিং জীবনকে সহজ ও স্বাচ্ছন্দ্যময় করে তুলবে।
স্টান্ডার্ড লাইব্রেরী ফাংশন ব্যবহারের ইতিবাচক দিক-সমূহ এখানে তুলে ধরা হলোঃ
সবচেয়ে গুরুত্বপূর্ণ বিষয় হলো এই ফাংশন-সমূহ আপনার প্রোগ্রামে ঠিক ঠাক কাজ করে।
এই ফাংশন-সমূহকে বিভন্ন পরীক্ষার মাধ্যমে নিখুঁতভাবে যাচাই করা হয়েছে।
ফাংশন-সমূহ " লাইব্রেরী ফাংশন" হওয়ায় একদল নিবেদিত ডেভেলপার নিয়মিত এগুলো আরোও নিখুঁত ও দ্রুতগতিসম্পন্ন করতে কাজ করে চলেছে।
এই প্রক্রিয়ায় তারা এই ফাংশন-সমূহকে সর্বোচ্চ পারফর্মেন্স সম্পন্ন করতে সক্ষম হয়।
যেহেতু সচরাচর ব্যবহৃত ইনপুট/আউটপুট ফাংশনসহ অন্যান্য ফাংশন যেমন- strlen(), sqrt(), pow() ইত্যাদি ইতিমধ্যেই লাইব্রেরীর অন্তর্ভুক্ত, সুতরাং নতুন করে এই ফাংশন-সমূহ আপনাকে আর লিখতে হবে না।
ইহা আপনার মূল্যবান সময় সাশ্রয়ের সাথে সাথে কাজের গতিও বাড়িয়ে দিবে।
প্রয়োজনের তাগিদে আপনার এপ্লিকেশনে পরিবর্তন আনলেও লাইব্রেরী ফাংশন-সমূহ সব সময় এবং সকল ক্ষেত্রেই একই রকম কাজ করে।
এই লাইব্রেরী ফাংশন-সমূহ সকল কম্পিউটারে একই রকম কাজ করে। ফলে ভিন্ন ভিন্ন কম্পিউটারের জন্য ভিন্ন ভিন্ন কোড লিখতে হবে না।
ইহা আপনার সময় ও শ্রম দুই-ই সাশ্রয় করে এবং আপনার প্রোগ্রামকে পুনর্ব্যবহারযোগ্য করে তোলে।
ধরুন, আপনি একটি সংখ্যার বর্গমূল নির্নয় করতে চাচ্ছেন।
এটা সম্পাদনের জন্য নিশ্চয় আপনাকে একগুছ কোড লিখতে হবে যা লিখতে আপনার সময় অপচয় হতে পারে এবং এমনকি বর্গমূল নির্ণয়ে আপনার কোড পর্যাপ্ত নাও হতে পারে।
যাইহোক আপনি সি প্রোগ্রামিং এ "math.h"
এর অধীনে sqrt()
ফাংশন ব্যবহার করে একটি সংখ্যার বর্গমূল নির্ণয় করতে পারেন।
#include <stdio.h>
#include <math.h>
int main()
{
float num, root;
printf("Enter a number to find square root.");
scanf("%f", &num);
// num এর বর্গমূল নির্নয় করে root এ জমা রাখি
root = sqrt(num);
printf("Square root of %.2f=%.2f", num, root);
return 0;
}
নাম | বর্ণনা |
---|---|
<assert.h> | Program assertion functions |
<ctype.h> | Character type functions |
<errno.h> | error handling functions |
<float.h> | floting point functions |
<limit.h> | limit setting functions |
<locale.h> | Localization functions |
<math.h> | Mathematics functions |
<setjmp.h> | Jump functions |
<signal.h> | Signal handling functions |
<stdarg.h> | Variable arguments handling functions |
<stddef.h> | various definitions functions |
<stdio.h> | Standard Input/Output functions |
<stdlib.h> | Standard Utility functions |
<string.h> | String handling functions |
<time.h> | Date time functions |
এই অধ্যায়ে আপনি সি প্রি-প্রসেসর (preprocessor) এবং ম্যাক্রো(macro) এর সাথে পরিচিত হবেন। এছাড়া কন্ডিশনাল(conditional), প্র্যাগমা(pragma), লাইন কন্ট্রোল(line control) ইত্যাদি প্রি-প্রসেসর কে আপনার প্রোগ্রামে ব্যবহার করা শিখবেন।
সি প্রি-প্রসেসর হলো ম্যাক্রো প্রি-প্রসেসর যা আপনার প্রোগ্রামে প্রি-প্রসেসর ডিফাইন্ড করার সম্মতি দেয় এবং যা কম্পাইলের পূর্বে আপনার প্রোগ্রামকে ট্রান্সফর্ম(transform) করে। এই ট্রান্সফরমেশনে হেডার ফাইল, ম্যাক্রো এক্সটেশন ইত্যাদি সংযুক্ত হতে পারে।
সকল প্রিপ্রোসেসর ডিরেক্টিভ(directive) #
প্রতীকের মাধ্যমে শুরু হয়।
#define PI 3.14
সি প্রোগ্রামে হেডার ফাইল সংযুক্তকরনের জন্য #include
প্রি-প্রসেসর যোগ করা হয়। উদাহরণস্বরূপঃ
#include <stdio.h>
এখানে "stdio.h"
হলো হেডার ফাইল। যা ফাংশন এবং ম্যাক্রো ডেফিনিশন নিয়ে গঠিত। #include
প্রি-প্রসেসর ডিরেক্টিভ(directive) উপরের লাইনকে stdio.h
হেডার ফাইলের কন্টেন্ট দ্বারা প্রতিস্থাপন করে।
scanf()
এবং printf()
ফাংশন ব্যবহারের পূর্বে #include <stdio.h>
প্রি-প্রসেসর ব্যবহার করার এটাই মূল কারণ।
আপনার প্রয়োজনে আপনিও নিজের মত করে বিভিন্ন ফাংশন ডেফিনিশন বিশিষ্ট হেডার ফাইল তৈরি করতে পারেন এবং প্রি-প্রসেসর ডিরেক্টিভ ব্যবহার করে আপনার প্রোগ্রামে সংযুক্ত করতে পারেন।
এই প্রি-প্রসেসর ডিরেক্টিভ-সমূহ সোর্স কোড কম্পাইল করার জন্য কন্ডিশনাল প্যারামিটার তৈরি করে যা সোর্স কোডের কম্পাইলকে নিয়ন্ত্রণ করে। এগুলো অবশ্যই ভিন্ন ভিন্ন লাইনে শুরু হতে হবে।
#if constant_expression
#else constant_expression
#endif
অথবা
#if constant_expression
#elif constant_expression
#endif
- যদি এবং কেবল যদি #if expression এর ভ্যালু true(non-zero) হয় তাহলে কম্পাইলার পরবর্তী কোড-সমূহকে কম্পাইল করে।
- কিন্তু যদি #if expression এর ভ্যালু false(0) হয় তাহলে কম্পাইলার পরবর্তী #else, #elif অথবা #endif পর্যন্ত লাইন-সমূহকে এড়িয়ে(skip করে) যায়।
- যদি #if expression এর ভ্যালু false(0) হয় এবং যদি কোনো #else এর ভ্যালু true(non-zero) হয় তাহলে #else এবং #endif এর মধ্যবর্তী লাইন-সমূহ কম্পাইল হয়।
- যদি #if expression এর ভ্যালু false(0) হয় এবং যদি কোনো #elif এর constant_expression এর ভ্যালু true(non-zero) হয় তাহলে #elif এবং #endif এর মধ্যবর্তী লাইন-সমূহ কম্পাইল হয়।
kt_satt_skill_example_id=584
উপরের উদাহরনে শুধুমাত্র "Hello SATT!" প্রিন্ট হয়েছে। কারণ #if এর ভ্যালু 1 হওয়ায় কম্পাইলার পরবর্তী কোড-সমূহকে এড়িয়ে গেছে।
kt_satt_skill_example_id=585
এখানেও শুধুমাত্র "Mango" প্রিন্ট হয়েছে। কারণ কেবল যদি প্রথম লাইন #if 0 হয় তাহলে "Orange" প্রিন্ট হবে।
#if OS==1
printf("Version 1.0");
#elif OS==2
printf("Version 2.0");
#else
printf("Version unknown");
#endif
OS এর সেটিং এর উপর ভিত্তিকরে প্রিন্ট হবে এবং যা #define প্রি-প্রসেসর দ্বারা ডিফাইন করা আছে।
#define এবং #undef প্রি-প্রসেসর ডিরেক্টিভ আইডেন্টিফায়ার(identifier) ডিফাইনে সম্মতি দিয়ে থাকে এবং যা নির্দিষ্ট ভ্যালু বহন করে। এসব আইডেন্টিফায়ার সাধারণত কন্সট্যান্ট অথবা ম্যাক্রো(macro) ফাংশন হতে পারে।
আইডেন্টিফায়ার ডিফাইন করা হয়েছে কিনা এই কন্ডিশন(condition) এর উপর ভিত্তিকরে #ifdef এবং #ifndef ডিরেক্টিভ(directive) নির্দিষ্ট সংখ্যক লাইনের কোডকে কম্পাইলের সম্মতি দিয়ে থাকে।
#define identifier replacement-code
#undef identifier
#ifdef identifier
#else or #elif
#endif
#ifndef identifier
#else or #elif
#endif
- #ifdef identifier এবং #if defined( identifier) একই অর্থে ব্যবহৃত হয়।
- #ifndef identifier এবং #if !defined(identifier) একই অর্থে ব্যবহৃত হয়।
- #define দ্বারা কোনো আইডেন্টিফায়ার ডিফাইন্ড করা হলে ইহা #undef এ না পৌঁছা পর্যন্ত সোর্স কোডের যেকোনো জায়গায় বিদ্যমান থাকে।
#define দ্বারা নিম্নোক্ত উপায়ে ম্যাক্রো(macro) ফাংশন ডিফাইন্ড করা করা যেতে পারেঃ
#define identifier(parameter-list) (replacement-text)
parameter-list এর ভ্যালু replacement-text এর ভ্যালু দ্বারা প্রতিস্থাপিত হয়।
#define PI 3.141
printf("%f",PI);
#define DEBUG
#ifdef DEBUG
printf("This is a debug message.");
#endif
#define QUICK(x) printf("%s\n",x);
QUICK("Hi!")
#define ADD(x, y) x + y
z=3 * ADD(4,5)
উপরের প্রোগ্রামের আউটপুট হবে 17। কারণ যোগের চেয়ে গুণের কাজ আগে হয়।
#define ADD(x,y) (x + y)
z=3 * ADD(4,5)
উপরের প্রোগ্রামের আউটপুট হবে 17। কারণ যোগফলকে বন্ধনীর মধ্যে রাখা হয়ছে এবং গুণের চেয়ে বন্ধনীর কাজ আগে হয়।
#include ডিরেক্টিভ(directive) এর মাধ্যমে এক্সটারনাল হেডার ফাইলকে কম্পাইলার দ্বারা প্রসেস করা যায়।।
#include <header-file>
অথবা
#include "source-file"
যখন কোনো ফাইলকে < এবং > এর মধ্যে রাখা হয় তখন কম্পাইলার(implementation) উক্ত ফাইলের জন্য পরিচিত হেডার ডিরেক্টরিতে( যা কম্পাইল কর্তৃক সংজ্ঞায়িত) অনুসন্ধান করে এবং ইহাকে কম্পাইল করে।
যখন ফাইলকে ডবল উদ্ধৃতি চিহ্নের মধ্যে রাখা হয় তখন সোর্স-ফাইলের সম্পূর্ণ বিষয়বস্তু(content) প্রতিস্থাপিত হয়। ফাইলের জন্য এই অনুসন্ধান পদ্ধতি বাস্তবায়ন-নির্দিষ্ট(implementation-specific)।
#include <stdio.h>
#include "my_header.h"
#line ডিরেক্টিভ(directive) বর্তমান লাইন নম্বর এবং বর্তমান সোর্স কোড ফাইলের নাম পরিবর্তনে সম্মতি দিয়ে থাকে।
#line line-number filename
উল্লেখ্য, যদি ফাইলের নাম না দেওয়া হয় তাহলে ইহা একই রকম থাকে। চলমান লাইন এর লাইন নম্বরটি নতুন লাইনের থেকে এক বশী হয়। সুতরাং প্রথম লাইন নম্বর 1
#line 50 user.c
#line 23
#error ডিরেক্টিভ(directive) কম্পাইল থামিয়ে দিয়ে নির্ধারিত error message রিটার্ন করে।
#error message
#ifndef VERSION
#error Version number not specified.
#endif
#pragma ডিরেক্টিভ ডিরেক্টিভকে ডিফাইন করার সম্মতি দেয়। ইহা কম্পাইলারের ক্রিয়াকলাপ নিয়ন্ত্রণ করে। যদি pragma সাপোর্ট না করে তাহলে ইহাকে এড়িয়ে যায়।
#pragma directive
ম্যাক্রো(macro) হলো এক খণ্ড কোড(a fragment of code) যার একটি নির্দিষ্ট নাম দেওয়া হয়। নাম ব্যবহার করেই আপনি এই কোড খণ্ডকে আপনার প্রোগ্রামে ব্যবহার করতে পারেন।
নিম্নোক্ত ম্যাক্র-সমূহ ইতিমধ্যেই কম্পাইলারে ডিফাইন্ড করা আছে এবং এগুলোকে পরিবর্তন করা যায় না।
পূর্বনির্ধারিত ম্যাক্রো | বর্ণনা |
---|---|
__LINE__ | চলমান লাইন নম্বরকে নির্দেশের জন্য ইন্টেজার(integer) কন্সট্যান্ট |
__FILE__ | চলমান সোর্স কোড ফাইলের নাম নির্দেশের জন্য স্ট্রিং |
__DATE__ | চলমান সোর্স ফাইলের কম্পাইল শুরু হওয়ার তারিখ নির্দেশের জন্য স্ট্রিং। asctime ফাংশন যে ফরম্যাটে তারিখ প্রদান করে ইহার ও ঠিক একই ফরম্যাট। যেমন- "mmm dd yyyy"। |
__TIME__ | চলমান সোর্স ফাইলের কম্পাইল শুরু হওয়ার সময় নির্দেশ এর জন্য স্ট্রিং। asctime ফাংশন যে ফরম্যাটে তারিখ প্রদান করে ইহার ও ঠিক একই ফরম্যাট। যেমন- "hh:mm:ss"। |
__STDC__ | যদি ANSI স্টান্ডার্ড অনুসরণ করে তাহলে ভ্যালু ১(non-zero) হবে। |
পূর্বনির্ধারিত ম্যাক্রো ব্যবহার করে চলমান সময় নির্ণয়ের জন্য সি প্রোগ্রাম
kt_satt_skill_example_id=596
এই অধ্যায়ে আপনি সি প্রোগ্রামিং ইনুমিরেশন(enumeration) নিয়ে কাজ করা শিখবেন। এছাড়া সি প্রোগ্রামিং এ enum
সচরাচর কোথায় ব্যবহৃত হয় তাও জানবেন।
সি প্রোগ্রামিং এ enumeration হলো ইউজার ডিফাইন্ড ডেটা টাইপ(user-defined data type) যা অখণ্ড পূর্ণসংখ্যা(integral constants) নিয়ে গঠিত হয়। ইনুমেরেশন(enumeration) ডিফাইন্ড করার জন্য enum
কীওয়ার্ড ব্যবহৃত হয়।
enum flag { constant1, constant2, ..., constantN };
এখানে flag হলো ইউজার ডিফাইন্ড ডেটা টাইপ
এবং constant1, constant2,...., constantN হলো flag টাইপের ভ্যালু।
ডিফল্টভাবে constant1 এর ভ্যালু 0, constant2 এর ভালু 1 এবং এভাবে চলতে থাকবে। আপনার প্রয়োজনে আপনি enum এলিমেন্টের ডিফল্ট ভ্যালু পরিবর্তন করতে পারেন।
অর্থাৎ আপনি চাইলে enum
এলিমেন্টের ইন্ডেক্স পরিবর্তন করতে পারেন।
// enum এর ডিফল্ট ভ্যালু পরিবর্তন
enum color {
red = 0,
green = 3,
black = 5,
white = 3,
};
আপনি যখন enum টাইপের ভ্যারিয়েবল তৈরি করেন তখন শুধুমাত্র ভ্যারিয়েবলের ব্লুপ্রিন্ট(blueprint) তৈরি হয়। নিচে enum টাইপের ভ্যারিয়েবল তৈরির পদ্ধতি দেখানো হলো
enum boolean { false, true };
enum boolean security;
এখানে enum boolean
টাইপের একটি security ভ্যারিয়েবল তৈরি হয়েছে।
এখানে ভিন্ন সিনট্যাক্স ব্যবহার করে একই security ভ্যারিয়েবল তৈরি করা হয়ছে।
enum boolean
{
false, true
} security;
kt_satt_skill_example_id=624
উপরের প্রোগ্রামে “week” কে ভ্যারিয়েবল হিসাবে ডিক্লেয়ার করা হয়েছে এবং tuesday কে “week” এর জন্য বরাদ্দ করা হয়েছে। তাই আমরা 2 আউটপুট পেয়েছি।
kt_satt_skill_example_id=626
উপরের প্রোগ্রামে Jan এর ইনিশিয়াল(initial) ভ্যালু 0 এবং Dec এর 11 হওয়ায় i = 0 থেকে i = 11 পর্যন্ত for লুপ রান(run) করবে।
সম্ভাব্য অনেক ভ্যালুর বিপরীতে Enum ভ্যারিয়েবল কেবল মাত্র একটি ভ্যালু গ্রহণ করে। নিচে উদাহরণের সাহায্যে ব্যাখ্যা করা হলোঃ
kt_satt_skill_example_id=628
ইহার কারণ integer এর সাইজ হলো 4 বাইট।
একই কাজ আপনি স্ট্রাকচার ব্যবহার করেও করতে পারেন। যাইহোক enum এর ব্যবহার আপনার কাজকে সহজ করে দিবে এবং যোগ্য প্রোগ্রামারের পরিচয় মিলবে।
flags(পতাকা) নিয়ে কাজ করার জন্য enum হতে পারে উত্তম পন্থা।
নিচে উদাহরণের সাহায্যে ব্যাখ্যা করা হলো
enum designFlags {
italics = 1,
border = 2,
color = 4
} button;
ধরুন আপনি উইন্ডোজ এপ্লিকেশনের জন্য একটি বাটন ডিজাইন করতে চাচ্ছেন। আপনি টেক্সট নিয়ে কাজ করার জন্য border, italics এবং color ফ্ল্যাগ(flag) সেট করতে পারেন।
উপরের প্রোগ্রাম(pseudocode) এ সকল অখণ্ড পূর্ণসংখ্যা(integral constant) কেন দুই এর সূচক তার একটি নির্দিষ্ট কারণ রয়েছে।
// বাইনারি মান
italics = 0000 0001
border = 0000 0010
color = 0000 0100
উপরের উদাহরনে অখণ্ড পূর্ণসংখ্যা যেহেতু ২ এর সূচক সুতরাং আপনি কোনো ধরণের দুশ্চিন্তা ছাড়াই বিটওয়াইজ OR(|) অপারেটর নিয়ে একই সঙ্গে দুই বা তার অধিক ফ্ল্যাগের সমন্বয় ঘটাতে পারেন।
নিচে উদাহরণের সাহায্যে ব্যাখ্যা করা হলোঃ
kt_satt_skill_example_id=631
যখন আউটপুট 5 হবে তখন আপনি অবশ্যই জানবেন যে, এখানে border এবং color ব্যবহৃত হয়েছে। কারণ border=1 এবং color=4 অর্থাৎ 4+1=5
এছাড়া আপনার প্রয়োজনেও আপনি flag যোগ করতে পারেন।
if (myDesign & italics) {
// italics এর জন্য ব্যবহৃত কোড
}
এখানে আমরা আমাদের ডিজাইনে italics যোগ করেছি। লক্ষ্য করলে দেখবেন শুধুমাত্র italics এর কোড if স্টেটমেন্টের মধ্যে লেখা হয়েছে।
সি প্রোগ্রামিং এ আপনি সবকিছু ইনুমিরেশন(enumeration) ছাড়াই করতে পারেন। কিন্তু কিছু কিছু পরিস্থিতিতে ইহা প্রোগ্রামকে সহজ করে দেয়। আর ইহাই দক্ষ প্রোগ্রামারদের মধ্যে পার্থক্য সৃষ্টি করে।
- দুটি enum এর নামে একই ভ্যালু থাকতে পারে।
kt_satt_skill_example_id=636
আমরা যদি বাহ্যকিভাবে enum কনস্ট্যান্টে কোনো ভ্যালু এসাইন না করি তাহলে কম্পাইলার ডিফল্টভাবে ইনডেক্স 0(শূন্য) দিয়ে শুরু করে ভ্যালু এসাইন(assaign) করে।
kt_satt_skill_example_id=638
কিছু কনস্ট্যান্টে আমরা যেকোনো অর্ডারের ভ্যালু এসাইন(assign) করতে পারি। যেসকল কনস্ট্যান্টে ভ্যালু এসাইন করা হয়না তারা পূর্ববর্তী ভ্যালুর সাথে এক যোগ করে ভ্যালু নিয়ে নেয়।
kt_satt_skill_example_id=639
- enum কনস্ট্যান্টে এসাইনকৃত ভ্যালু অবশ্যই অখণ্ড পূর্ণসংখ্যর(integeral constant) হতে হবে। অর্থাৎ এসাইনকৃত ভ্যালু অবশ্যই একটি সম্ভাব্য সর্বনিম্ন পূর্ণসংখ্যা থেকে সর্বোচ্চ পূর্ণসংখ্যার পরিসর(range) এর মধ্যে থাকতে হবে।
- স্ব-স্ব স্কোপে(scope) সকল enum কনস্ট্যান্ট(constant)-কে অবশ্যই অনন্য(unique) হতে হবে।
kt_satt_skill_example_id=641
সি প্রোগ্রামিং এ যেহেতু এরর হ্যান্ডেলিং(error handling) এর সরাসরি কোনো সাপোর্ট নাই, কিন্তু সিস্টেম প্রোগ্রামিং ল্যাংগুয়েজ হওয়ায় ইহা রিটার্ন ভ্যালুর উপর ভিত্তিকরে lower level এ এক্সেস সরবরাহ করে।
যেকোনো error এর জন্য অধিকাংশ সি অথবা এমনকি ইউনিক্স ফাংশন কল(call) এর ক্ষেত্রেও -1
অথবা NULL
রিটার্ন করে এরর কোড হিসাবে errno
সেট করে। ইহা গ্লোবাল ভ্যারিয়েবল হিসাবে সেট হয় এবং এই সংকেত দেয় যে, ফাংশন কল করার সময় এরর সংঘটিত হয়েছে।
আপনি বিভিন্ন ধরনের এরর কোড দেখে থাকতে পারেন যা <errno.h>
হেডার ফাইলে ডিফাইন্ড করা আছে।
সুতরাং একজন সি প্রোগ্রামার রিটার্ন ভ্যালু চেক করতে পারে এবং রিটার্ন ভ্যালুর উপর ভিত্তিকরে উপযুক্ত কাজটি করতে সক্ষম হয়।
প্রোগ্রাম ইনিশিয়ালাইজিং(initializing) এর সময় errno
এর ভ্যালু 0(zero) সেট করা খুবই ভাল। ভ্যালু 0 ইঙ্গিত দেয় যে, প্রোগ্রামে কোনো এরর নাই।
সি প্রোগ্রামিং ল্যাংগুয়েজ perror()
এবং strerror()
দুটি ফাংশন সরবরাহ করে যার মাধ্যমে errno
এর সাথে সম্পর্কযুক্ত টেক্সট মেসেজ প্রদর্শন করানো যেতে পারে।
perror()
ফাংশনটি , এর মধ্য দিয়ে যে স্ট্রিং অতিক্রম করানো হয় তা, এরপরে কোলন(:) এবংerrno
এর চলমান ভ্যালুর জন্য পাঠ্য উপস্থাপনা(textual representation)-কে প্রদর্শন করায়।strerror()
ফাংশনটিerrno
এর চলমান ভ্যালুর পাঠ্য উপস্থাপনা(textual representation) এর পয়েন্টার রিটার্ন করে।
চলুন, বিদ্যমান নাই এমন একটি ফাইল খোলার(open) চেষ্টা করি এবং এর মাধ্যমে একটি ত্রুটির(error) সৃষ্টি করি। ব্যবহার দেখানোর জন্য এখানে আমরা উভয় ফাংশন-ই ব্যবহার করেছি।
কিন্তু আপনি ত্রুটি দেখানোর জন্য আপনার পছন্দমত এক বা তার অধিক পদ্ধতি ব্যবহার করতে পারেন। দ্বিতীয় গুরুত্বপূর্ণ বিষয় হলো ফাইল প্রবাহের(stream) সমস্ত ত্রুটিসমূহের(errors) আউটপুট নেওয়ার জন্য stderr
ব্যবহার করা উচিৎ।
উদাহারন ১:
kt_satt_skill_example_id=654
প্রায় সময়েই প্রোগ্রামাররা কোনো সংখ্যাকে ভাগ করার সময় ভাজক শূন্য কিনা চেক করে দেখে না যা পরিশেষে রান টাইম এরর(runtime error) উৎপন্ন করে।
নিচের প্রোগ্রামে ভাগ করার পূর্বে ভাজক 0(শূন্য) কিনা চেক করার মাধ্যমে এই সমস্যার সমাধান দেখানো হলোঃ
উদাহারন ২:
kt_satt_skill_example_id=658
প্রোগ্রামের কাজ সফলভাবে সম্পন্ন হওয়ার পরে প্রোগ্রাম থেকে সচারচর EXIT_SUCCESS
এর স্ট্যাটাস ভ্যালু নিয়ে বের হয়। এখানে EXIT_SUCCESS
হলো ম্যাক্রো এবং ইহাকে 0 দ্বারা সঙ্গায়িত করা হয়।
আপনার প্রোগ্রামে যদি কোনো এরর(error) থাকে এবং আপনি যদি প্রোগ্রাম থেকে বের হতে চান তাহলে EXIT_FAILURE
স্ট্যাটাস নিয়ে আপনাকে প্রোগ্রাম থেকে বের হতে হবে যাকে -1 দ্বারা সঙ্গায়িত করা হয়।
সুতরাং চলুন উপরের প্রোগ্রামকে নিচের মত করে লিখিঃ
kt_satt_skill_example_id=659
এই অধ্যায়ে আপনি টাইপ কাস্টিং সম্বন্ধে জানবেন। টাইপ কাস্টিং এর মাধ্যমে আপনি ডেটাকে এক টাইপ হতে অন্য টাইপে রূপান্তর করতে পারবেন।
টাইপ কাস্টিং(Type casting) এর মাধ্যমে এক ডেটা টাইপকে অন্য ডেটা টাইপে রূপান্তর করা যায়। সি প্রোগ্রামিং এ টাইপ কাস্টিং এর জন্য আমরা কাস্ট অপারেটর ব্যবহার করি এবং ইহাকে (type) এর মাধ্যমে প্রকাশ করা হয়।
(type)value;
নোটঃ আমরা আপনাকে সবসময় নিম্ন ভ্যালু থেকে উচ্চ ভ্যালুতে রূপান্তরের সুপারিশ করছি। তাহলে ডেটা হারানোর ভয় থাকবে না।
টাইপ কাস্টিং ছাড়াঃ
int f= 5/2;
printf("f : %d\n", f );//আউটপুট: 2
টাইপ কাস্টিংসহ
float f=(float) 5/2;
printf("f : %f\n", f );//Output: 2.500000
উদাহরনঃ টাইপ কাস্টিং এর ব্যবহার
চলুন int ভ্যালুকে float এ কাস্ট করার একটি উদাহরণ দেখিঃ
kt_satt_skill_example_id=662
এই অধ্যায়ে আপনি সি প্রোগ্রামে বিট ফিল্ডের ব্যবহার শিখবেন। বিট ফিল্ড ব্যবহার করে মেমোরির সদ্ব্যবহার করা যায়।
ধরুন, আপনার প্রোগ্রামে কিছু TRUE/FALSE ভ্যারিয়েবল রয়েছে যেগুলোকে structure ভ্যারিয়েবলের মাধ্যমে শ্রেণীবদ্ধ করা হয়েছে এবং এই স্ট্রাকচার ভ্যারিয়েবলের নাম status রাখা হয়েছে।
উদাহরণস্বরুপঃ
struct {
unsigned int widthValidated;
unsigned int heightValidated;
} status;
এই স্ট্রাকচার ভ্যারিয়েবলের জন্য মেমোরিতে ৮ বাইট জায়গা/স্পেসের প্রয়োজন হবে। কিন্তু আমরা প্রত্যেক ভ্যারিয়েবলের জন্য হয় 0 অথবা 1 স্টোর করতে যাচ্ছি। এ ধরনের পরিস্থিতিতে মেমোরির সঠিক ব্যবহারের জন্য সি প্রোগ্রামিং ল্যাংগুয়েজ উত্তম পন্থা সরবরাহ করে।
আপনি যদি স্ট্রাকচারের মধ্যে এধরণের ভ্যারিয়েবল ব্যবহার করে থাকেন তাহলে আপনি এই ভ্যারিয়েবল-সমূহের মেমোরি সাইজ বা বাইট-সংখ্যাও নির্ধারণ করে দিতে পারেন যা কম্পাইলারকে আগাম বার্তা দিবে যে, আপনি এই ভ্যারিয়েবল-সমূহের জন্য শুধুমাত্র ঐ সংখ্যক বাইট ব্যবহার করতে যাচ্ছেন।
উদারণস্বরুপঃ উপরের স্ট্রাকচারকে নিম্মের ন্যায়ও লেখা যেতে পারেঃ
struct {
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;
উপরের স্ট্রাকচার ভ্যারিয়েবলটি মেমোরিতে ৪(চার) বাইট জায়গা দখল করবে, কিন্তু ভ্যালু স্টোর করার জন্য শুধুমাত্র ২ বাইট জায়গা লাগবে।
আপনি যদি ৩২টি পর্যন্ত ভ্যারিয়েবল ব্যবহার করে থাকেন এবং প্রত্যেকের সাইজ যদি ১ বিট করে হয় তবুও স্ট্রাকচার ভ্যারিয়েবল status মেমোরিতে ৪(চার) বাইট জায়গা দখল করবে। যাইহোক, ভ্যারিয়েবলের সংখ্যা ৩৩ হওয়া মাত্র ইহা পরবর্তী মেমোরি স্লট বরাদ্দ করবে যা ৮ বাইট মেমোরি ব্যবহার করা শুরু করবে।
চলুন নিচের উদাহরণের মাধ্যমে আরো স্বচ্ছ ধারণা নিইঃ
kt_satt_skill_example_id=668
স্ট্রাকচারের মধ্যে বিট ফিল্ডকে নিম্নের ন্যায় ডিক্লেয়ার করা হয়ঃ
struct {
type member_name : size ;
};
নিম্নে বিট ফিল্ডের এলিমেন্ট-সমূহকে বর্ণনা করা হলোঃ
- type: হলো বিট ফিল্ড ভ্যারিয়েবলের টাইপ। টাইপ সাধারণত
int
,signed
অথবাunsigned int
হতে পারে।- member_name বিট ফিল্ড ভ্যারিয়েবলের নাম
- size বিট ফিল্ড ভ্যারিয়েবলের বিটের সংখ্যা। নির্ধারিত টাইপের বিট সাইজ থেকে এই সাইজ অবশ্যই ছোট হতে হবে।
পূর্ব নির্ধারিত সাইজ ব্যবহার করে ভ্যারিয়েবল ডিফাইন্ড করাকে বিট ফিল্ড বলা হয়। একটি বিল্ট ফিল্ড একের অধিক বিট ধারন করতে পারে।
উদাহরণস্বরুপ, আপনার যদি এমন একটি ভ্যারিয়েবলের প্রয়োজন হয় যা 0 থেকে 7 পর্যন্ত ভ্যালু স্টোর করতে পারে তাহলে আপনি নিম্নের ন্যায় ৩বিটের একটি বিট ফিল্ড ভ্যারিয়েবল ডিফাইন করতে পারেনঃ
struct {
unsigned int age : 3;
} Student;
উপরের প্রোগ্রামটি সি কম্পাইলারকে এই নির্দেশনা দেয় যে age ভ্যারিয়েবল তার ভ্যালু স্টোর করার জন্য ৩ বিট ব্যবহার করতে চলেছে। কিন্তু আপনি যদি ৩ বিটের চেয়ে বেশি ব্যবহার করতে চান তাহলে ইহা আপনাকে এই কাজের জন্য সম্মতি দিবে না।
চলুন নিচের উদাহরণের সাহায্যে প্রয়োগ দেখে নিইঃ
kt_satt_skill_example_id=669
এই অধ্যায়ে আপনি সি ভ্যারিয়েবল আর্গুমেন্ট সম্মন্ধে জানবেন। ভ্যারিয়েবল আর্গুমেন্ট বলতে পূর্ব-নির্ধারিত আর্গুমেন্টের পরিবর্তে যেকোনো সংখ্যক আর্গুমেন্টকে বুঝানো হয়।
কিছু কিছু ক্ষেত্রে আপনি এমন সব ফাংশনের আকাংখ্যা করতে পারেন যা আপনার প্রয়োজন অনুসারে আর্গুমেন্ট (variable number of argument) গ্রহণ করতে পারে। অর্থাৎ পূর্ব-নির্ধারিত প্যারামিটারের পরিবর্তে যেন যেকোনো সংখ্যক প্যারামিটার গ্রহণ করতে পারে।
সি প্রোগ্রামিং আপনাকে এই সমস্যার সমাধান সরবরাহ করে। সি প্রোগ্রামিং আপনাকে এমন একটি ফাংশন ডিফাইন করার সুযোগ দেয় যা আপনার প্রয়োজন অনুসারে আর্গুমেন্ট গ্রহণ করতে পারে।
নিচের উদাহরণের সাহায্যে এধরনের ফাংশনের ব্যাখ্যা করা হলোঃ
int functionName(int, ... ) {
.
.
.
}
int main() {
functionName(1, 2, 3);
functionName(1, 2, 3, 4);
.
.
.
}
উপরের সিন্ট্যাক্সে লক্ষ্য করলে দেখবেন যে, তিনটি ডট(...) চিহ্নকে functionName() ফাংশনের শেষ আরগুমেন্ট হিসাবে ব্যবহার করা হয়েছে। ডট(.) চিহ্নের আগে সর্বদাই একটি int
ব্যবহার করা হয় যা মোট ভ্যারিয়েবল আরগুমেন্টের সংখ্যাকে বুঝায়। এ ধরণের বৈশিষ্ট্য ব্যবহার করার জন্য আপনাকে আপনার প্রোগ্রামে stdarg.h
হেডার ফাইল সংযুক্ত করতে হবে যার মধ্যে এই বৈশিষ্ট্যাবলীর ফাংশন এবং ম্যাক্রো ডিফাইন করা আছে।
kt_satt_skill_example_id=678
উপরের প্রোগ্রামে লক্ষ্য করলে দেখবেন যে, average() ফাংশনটিকে দুইবার কল করা হয়েছে এবং উভয় ক্ষেত্রেই প্রথম আর্গুমেন্ট দ্বারা ফাংশনের মধ্য দিয়ে অতিক্রম হওয়া ভ্যারিয়েবল আর্গুমেন্টের সংখ্যা বুঝানো হয়েছে। ফাংশনের মধ্য দিয়ে ভ্যারিয়েবল সংখ্যক আর্গুমেন্ট অতিক্রম করানোর জন্য শুধুমাত্র ডট(.) চিহ্ন ব্যবহৃত হয়।
সি প্রোগ্রাম সম্পাদনের সময় আপনি কমান্ড লাইন থেকেও ভ্যালু অতিক্রম(pass) করাতে পারেন। এই ভ্যালুকে কমান্ড লাইন আর্গুমেন্ট(command line argument) বলা হয়। অনেক সময়ই ইহা প্রয়োজন হতে পারে, বিশেষকরে আপনি যখন প্রোগ্রামের ভিতরে এই ভ্যালু-সমূহকে হার্ড কোড(hard code) না করে বাহির থেকে কন্ট্রোল করতে চাইবেন।
main(int argc, char *argv[] ) ফাংশন ব্যবহার করে কমান্ড লাইন আর্গুমেন্টকে পরিচালনা করা হয়, যেখানে argc ফাংশনের মধ্য দিয়ে অতিক্রম হওয়া আর্গুমেন্টের সংখ্যাকে নির্দেশ করে এবং argv[] হলো পয়েন্টার অ্যারে যা প্রোগ্রামের মধ্য দিয়ে অতিক্রম হওয়া প্রত্যেক আর্গুমেন্টকে নির্দেশ করে।
kt_satt_skill_example_id=680
নোটঃ আপনার জেনে রাখা উচিৎ যে, argv[0]
দ্বারা প্রোগ্রাম তার নিজের নামকে নির্দেশ করে, argv[1]
দ্বারা সরবরাহকৃত প্রথম কমান্ড লাইন আর্গুমেন্টকে নির্দেশ করে এবং *argv[n]
দ্বারা সবশেষ আর্গুমেন্টকে নির্দেশ করে। যদি কোনো আর্গুমেন্ট সরবরাহ করা না হয় তাহলে argc
হবে 1 এবং যদি একটি আর্গুমেন্ট অতিক্রম করানো হয় তাহলে argc
হবে 2।
আর্গুমেন্ট-সমূহকে প্রোগ্রামের মধ্য দিয়ে অতিক্রম করানোর সময় স্পেসের মাধ্যমে পৃথক করা হয়। কিন্তু আর্গুএমেন্টের নিজের-ই যদি স্পেস থাকে তাহলে আর্গুমেন্ট-সমূহকে সিঙ্গেল('') অথবা ডাবল(" ") কোটেশনের মধ্যে রাখা হয়।
চলুন, উপরের প্রোগ্রামটি আমরা পুনরায় লিখি যার মাধ্যমে আমরা প্রোগ্রামের নাম প্রিন্ট করবো এবং কমান্ড লাইন আর্গুমেন্টকে সিঙ্গেল কোটেশনের মধ্যে রেখে অতিক্রম করাবোঃ
kt_satt_skill_example_id=682
common.read_more